#include"CentralCache.h"
#include"PageCache.h"

CentralCache CentralCache::_sInst;

//从中心缓存获取一定的对象数量给thread cache
size_t CentralCache::FetchRangobj(void*& start,void*& end,size_t batchnum,size_t size)   
{
    size_t index = Sizeclass::Index(size);
    _spanlists[index].mtx.lock();//加锁

    Span* span = GetOneSpan(_spanlists[index],size);
    assert(span);
    assert(span->_freelist);

    //从span中获取batchnum个对象，不够的话有多少个就给多少个
    start = span->_freelist;
    end = start;
    size_t i = 0;
    size_t actualnum = 1;
    while(ListNext(end) != nullptr && i < batchnum - 1)
    {
        end = ListNext(end);
        i++;
        actualnum++;
    }
    span->_freelist = ListNext(end);
    ListNext(end) = nullptr;

    _spanlists[index].mtx.unlock();//解锁
    return actualnum;
}

//获取一个非空的span
Span* GetOneSpan(SpanList& list,size_t size)
{
    //查看当前spanlist中是否还有未分配的对象的span
    Span* it = list.Begin();
    while(it != list.End())
    {
        if(it->_freelist != nullptr)
        {
            return it;
        }
        else
        {
            it = it->next;
        }
    }

    //先把central cache的桶锁解开,这样其他线程释放内存对象回来，不会阻塞
    list.mtx.unlock();

    //走到这里说明没有空闲span可以分配了，只能向pagecache申请
    PageCache::GetInstance()->_PageMTX.lock();
    Span* span = PageCache::GetInstance()->NewSpan(Sizeclass::NumMovePage(size));
    span->_isUse = true;
    span->_objsize = size;
    PageCache::GetInstance()->_PageMTX.unlock();

    
    //对获取span进行切分,不需要加锁，因为此时其他线程访问不到这个span
 
    //计算大块内存的地址和大小
    char* start = (char*)(span->Page_Id << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    char* end = start + bytes;

    //把大块内存用freelist连接起来
    span->_freelist = start;
    void* tail = start;
    start += size;
    int i = 1;
    while(start < end)
    {
        ++i;
        ListNext(tail) = start;
        tail = ListNext(tail);  
        start += size;
    }
    ListNext(tail) = nullptr;
     
    //切好以后，把span挂到桶下面再加锁
    list.mtx.lock();
    list.Pushfront(span);
    
    return span;
}

void CentralCache::ReleaseListToSpans(void* start,size_t size)
{
    size_t index = Sizeclass::Index(size);
    _spanlists[index].mtx.lock();
    while(start)
    {
        void* next = ListNext(start);

        Span* span = PageCache::GetInstance()->MapObjectToSpan(start);

        ListNext(start) = span->_freelist;
        span->_freelist = start;
        span->count--;
        //说明所有小内存都回来了，这个span就可以回收给pagecache，pagecache可以再去做分配
        if(span->count == 0)
        {   
            _spanlists[index].Erase(span);
            span->next = nullptr;
            span->prev = nullptr;
            span->_freelist = nullptr;

            //把桶锁解开
            _spanlists[index].mtx.unlock();

            PageCache::GetInstance()->_PageMTX.lock();
            PageCache::GetInstance()->ReleaseSpanToPageCache(span);
            PageCache::GetInstance()->_PageMTX.unlock();

            //重新上锁
            _spanlists[index].mtx.lock();
        }

        start = next;
    }


    _spanlists[index].mtx.unlock();
}
